﻿// <copyright file="PackageContainer.cs" company="Sinclair Community College">
// Copyright 2010 Sinclair Community College
// </copyright>
// <originalAuthor>Patrick S. Seymour (patrick.seymour@gmail.com)</originalAuthor>

namespace SinclairCC.AppV
{
    using System;
    using System.Collections.Generic;
    using System.IO;

    /// <summary>
    /// The Package Container is used to track a set of files, directories, and other information
    /// specific to an App-V package.
    /// </summary>
    public class PackageContainer : Container
    {
        // TODO: The members here, except for the StorageBlockInfoTable, should be combined into a class called _PACKAGE_INFO, to better match the spec.

        /// <summary>
        /// A unique identifier of the package (GUID).
        /// </summary>
        private Guid packageGuid;

        /// <summary>
        /// Indicates last time the package was accessed.
        /// </summary>
        private DateTime accessTime;

        /// <summary>
        /// The sum of the sizes of all files that are part of the package.
        /// </summary>
        private ulong packageSize;

        /// <summary>
        /// The actual size of the launch specific data required for the launch of the package.
        /// </summary>
        private ulong launchSize;

        /// <summary>
        /// The size of package file data resident in FSD Cache Volume File.
        /// </summary>
        private ulong cachedFileSize;

        /// <summary>
        /// The size of package launch specific file data resident in FSD Cache Volume File.
        /// </summary>
        private ulong cachedLaunchSize;

        /// <summary>
        /// The percentage of the launch data cached.
        /// </summary>
        private uint percentLaunch;

        /// <summary>
        /// The percentage of the total data cached.
        /// </summary>
        private uint percentTotal;

        /// <summary>
        /// Indicates a version of this package; used during package upgrade.
        /// </summary>
        private uint packVersion;

        /// <summary>
        /// Name of the package.
        /// </summary>
        private string packageName; /* 128 bytes */

        /// <summary>
        /// Indicates a version of package metadata; used during package upgrade.
        /// </summary>
        private uint dirVersion;

        /// <summary>
        /// Package version GUID; used during package upgrade.
        /// </summary>
        private Guid packVersionGuid;

        /// <summary>
        /// Indicates package rank relative to other packages (Obsolete).
        /// </summary>
        private uint rank;

        /// <summary>
        /// The length of the buffer that consists of metadata descriptors for all files and
        /// directories that belong to the package.
        /// </summary>
        private uint metadataLength;

        /// <summary>
        /// The length of the buffer that consists of security descriptors for all files and
        /// directories that belong to the package.
        /// </summary>
        private uint securityDataLength;

        /// <summary>
        /// The size of the data storage block used by App-V streaming server.
        /// </summary>
        private uint serverBlockSize;

        /// <summary>
        /// Indicates how many instances of the package are opened.
        /// </summary>
        private uint reference;

        /// <summary>
        /// Package-specific flags.
        /// </summary>
        private PackageFlags flags;

        /// <summary>
        /// Used during package upgrade.
        /// </summary>
        private uint versionHistoryCount;

        /// <summary>
        /// Used during package upgrade.
        /// </summary>
        private uint versionHistoryBufferIndex;

        private StorageBlockInfoTable storageBlockTable;

        /// <summary>
        /// Initializes a new instance of the ContainerPackage class.
        /// </summary>
        public PackageContainer(System.IO.BinaryReader fileReader)
            : base(fileReader)
        {
            this.storageBlockTable = null;
            this.ReadContainer(fileReader);
        }

        /// <summary>
        /// Reads the actual data for this container from the file.
        /// </summary>
        protected void ReadContainer(System.IO.BinaryReader fileReader)
        {
            byte[] guidBytes = fileReader.ReadBytes(16);
            this.packageGuid = new Guid(guidBytes);

            long fileTimeLong = fileReader.ReadInt64();
            try
            {
                this.accessTime = DateTime.FromFileTimeUtc(fileTimeLong);
            }
            catch (ArgumentOutOfRangeException) { }
            /* this.accessTime = fileReader.ReadUInt64(); */

            this.packageSize = fileReader.ReadUInt64();
            this.launchSize = fileReader.ReadUInt64();
            this.cachedFileSize = fileReader.ReadUInt64();
            this.cachedLaunchSize = fileReader.ReadUInt64();
            this.percentLaunch = fileReader.ReadUInt32();
            this.percentTotal = fileReader.ReadUInt32();
            this.packVersion = fileReader.ReadUInt32();

            byte[] stringBytes;
            System.Text.UnicodeEncoding encoding = new System.Text.UnicodeEncoding();

            stringBytes = fileReader.ReadBytes(128);
            this.packageName = encoding.GetString(stringBytes);

            this.dirVersion = fileReader.ReadUInt32();

            guidBytes = fileReader.ReadBytes(16);
            this.packVersionGuid = new Guid(guidBytes);

            this.rank = fileReader.ReadUInt32();
            this.metadataLength = fileReader.ReadUInt32();
            this.securityDataLength = fileReader.ReadUInt32();
            this.serverBlockSize = fileReader.ReadUInt32();
            this.reference = fileReader.ReadUInt32();
            this.flags = (PackageFlags)fileReader.ReadUInt32();
            this.versionHistoryCount = fileReader.ReadUInt32();
            this.versionHistoryBufferIndex = fileReader.ReadUInt32();
            this.storageBlockTable = new StorageBlockInfoTable(fileReader);
        }

        /// <summary>
        /// A unique identifier of the package (GUID).
        /// </summary>
        public Guid PackageGuid
        {
            get { return this.packageGuid; }
        }

        /// <summary>
        /// Indicates last time the package was accessed.
        /// </summary>
        public DateTime AccessTime
        {
            get { return this.accessTime; }
        }
        
        /// <summary>
        /// Gets the sum of the sizes of all files that are part of the package.
        /// </summary>
        public ulong PackageSize
        {
            get { return this.packageSize; }
        }

        /// <summary>
        /// The actual size of the launch specific data required for the launch of the package.
        /// </summary>
        public ulong LaunchSize
        {
            get { return this.launchSize; }
        }

        /// <summary>
        /// The size of package file data resident in FSD Cache Volume File.
        /// </summary>
        public ulong CachedFileSize
        {
            get { return this.cachedFileSize; }
        }

        /// <summary>
        /// The size of package launch specific file data resident in FSD Cache Volume File.
        /// </summary>
        public ulong CachedLaunchSize
        {
            get { return this.cachedLaunchSize; }
        }

        /// <summary>
        /// The percentage of the launch data cached.
        /// </summary>
        public uint PercentLaunch
        {
            get { return this.percentLaunch; }
        }

        /// <summary>
        /// The percentage of the total data cached.
        /// </summary>
        public uint PercentTotal
        {
            get { return this.percentTotal; }
        }

        /// <summary>
        /// Indicates a version of this package; used during package upgrade.
        /// </summary>
        public uint PackageVersion
        {
            get { return this.packVersion; }
        }

        /// <summary>
        /// Name of the package.
        /// </summary>
        public string PackageName
        {
            get { return this.packageName; }
        }

        /// <summary>
        /// Indicates a version of package metadata; used during package upgrade.
        /// </summary>
        public uint DirVersion
        {
            get { return this.dirVersion; }
        }

        /// <summary>
        /// Package version GUID; used during package upgrade.
        /// </summary>
        public Guid PackageVersionGuid
        {
            get { return this.packVersionGuid; }
        }

        /// <summary>
        /// Indicates package rank relative to other packages (Obsolete).
        /// </summary>
        public uint Rank
        {
            get { return this.rank; }
        }

        /// <summary>
        /// The length of the buffer that consists of metadata descriptors for all files and
        /// directories that belong to the package.
        /// </summary>
        public uint MetadataLength
        {
            get { return this.metadataLength; }
        }

        /// <summary>
        /// The length of the buffer that consists of security descriptors for all files and
        /// directories that belong to the package.
        /// </summary>
        public uint SecurityDataLength
        {
            get { return this.securityDataLength; }
        }

        /// <summary>
        /// The size of the data storage block used by App-V streaming server.
        /// </summary>
        public uint ServerBlockSize
        {
            get { return this.serverBlockSize; }
        }

        /// <summary>
        /// Indicates how many instances of the package are opened.
        /// </summary>
        public uint Reference
        {
            get { return this.reference; }
        }

        /// <summary>
        /// Package-specific flags.
        /// </summary>
        public PackageFlags Flags
        {
            get { return this.flags; }
        }

        /// <summary>
        /// Used during package upgrade.
        /// </summary>
        public uint VersionHistoryCount
        {
            get { return this.versionHistoryCount; }
        }

        /// <summary>
        /// Used during package upgrade.
        /// </summary>
        public uint VersionHistoryBufferIndex
        {
            get { return this.versionHistoryBufferIndex; }
        }

        public StorageBlockInfoTable StorageBlockTable
        {
            get { return this.storageBlockTable; }
        }

        public void Process(uint containerSize, uint storageBlockSize, System.IO.BinaryReader fileReader)
        {
            long position = fileReader.BaseStream.Position;
            foreach (StorageBlockInfo sbi in this.storageBlockTable)
            {
                long offset = sbi.GetOffset(containerSize, storageBlockSize);
                if (offset != -1)
                {
                    // Seek to the container's offset.
                    fileReader.BaseStream.Seek(offset, SeekOrigin.Begin);

                    byte[] buffer = new byte[storageBlockSize];
                    int bytesRead = fileReader.Read(buffer, 0, buffer.Length);

                    MemoryStream byteStream = new MemoryStream(buffer, false);
                    System.IO.BinaryReader bufferReader = new System.IO.BinaryReader(byteStream);

                    PackageFileGuidTable pfgt = new PackageFileGuidTable(bufferReader);
                                            
                    bufferReader.Close();
                    byteStream.Close();
                    byteStream.Dispose();
                }
            }
            fileReader.BaseStream.Seek(position, SeekOrigin.Begin);
        }

    }
}
